﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SqlServer.Types;
using System.Data.SqlTypes;

namespace MyFloridaHouse.DataAccess.SQLServerTypes
{
    //General tools for working w/ the polygon class
    public static class PolygonTools
    {
        public static List<Polygon> CleanPolygons(List<Polygon> polygons)
        {
            List<Polygon> listToReturn = new List<Polygon>();
            foreach (Polygon poly in polygons)
            {
                SqlGeometry startPt = poly.polyPoints[0];
                SqlGeometry prevPt = startPt;
                int ptInx = 0;
                List<SqlGeometry> workingPolyPointList = new List<SqlGeometry>();
                foreach (SqlGeometry pt in poly.polyPoints) //exterior
                {
                    if (!pt.STEquals(prevPt))
                    {
                        //poly.polyPoints.RemoveAt(ptInx);
                        workingPolyPointList.Add(pt);
                    }
                    prevPt = pt;
                    ptInx++;
                }
                var uniquePolyPointList = workingPolyPointList.Distinct(new DistinctPointComparer());
                poly.polyPoints = uniquePolyPointList.ToList();
                if (poly.polyPoints.Count() > 3) //this one is reduced out at this zoom level
                {
                    if (!poly.polyPoints[0].STEquals(startPt))//verify we keep start point the same w/ out wiping out current point in 0 spot
                    {
                        poly.polyPoints.Insert(0, startPt);
                    }
                    if (!poly.polyPoints[poly.polyPoints.Count - 1].STEquals(startPt))//verify we keep end point the same
                    {
                        poly.polyPoints.Add(startPt);
                    }
                    listToReturn.Add(poly);
                }
                if (poly.polyPoints.Count() > 3) //this one is reduced out at this zoom level
                {
                    List<interiorRing> irListToReturn = new List<interiorRing>();
                    foreach (interiorRing ir in poly.irRingList) //interiors
                    {
                        startPt = ir.irPoints[0];
                        prevPt = startPt;
                        int irIndx = 0;
                        List<SqlGeometry> workingIRPointList = new List<SqlGeometry>();
                        foreach (SqlGeometry pt in ir.irPoints) //exterior
                        {
                            if (!pt.STEquals(prevPt))
                            {
                                //ir.irPoints.RemoveAt(irIndx);
                                workingIRPointList.Add(pt);
                            }
                            prevPt = pt;
                            irIndx++;
                        }
                        //ir.irPoints = workingIRPointList;
                        var uniqueirPointList = workingIRPointList.Distinct(new DistinctPointComparer());
                        ir.irPoints = uniqueirPointList.ToList();
                        if (ir.irPoints.Count() > 3)
                        {
                            if (!ir.irPoints[0].STEquals(startPt))//verify we keep start point
                            {
                                //ir.irPoints[0] = startPt; //jdm this was a mistake fixed on 3/24 
                                ir.irPoints.Insert(0, startPt);
                            }
                            if (!ir.irPoints[ir.irPoints.Count - 1].STEquals(startPt))//verify we keep end point the same
                            {
                                ir.irPoints.Add(startPt);
                            }
                            irListToReturn.Add(ir);
                        }
                    } //foreach (interiorRing ir in poly.irRingList)
                    poly.irRingList.Clear();
                    poly.irRingList = irListToReturn;
                }
            } //foreach (polygon poly in polygons)
            return listToReturn;
        }


        public static List<Polygon> GetPolygons(SqlGeometry geom, int level)
        {
            SqlInt32 numOfGeoms = geom.STNumGeometries();
            List<Polygon> listOfPolys = new List<Polygon>();
            for (int ng = 1; ng <= numOfGeoms; ng++)//looping through each geom
            {
                Polygon newPolyForList = new Polygon();
                SqlGeometry individualGeom = geom.STGeometryN(ng);//individual polygon
                newPolyForList.polyPoints = GetPolyPoints(individualGeom, level, true);
                newPolyForList.irRingList = GetInteriorRings(individualGeom, level);
                listOfPolys.Add(newPolyForList);
            }
            return listOfPolys;
        }


        public static List<SqlGeometry> GetPolyPoints(SqlGeometry individualGeom, int level, bool isExterior)
        {
            SqlGeometry ring;
            if (isExterior)
            {
                ring = individualGeom.STExteriorRing();
            }
            else
            {
                ring = individualGeom;
            }

            List<SqlGeometry> processedPoints = new List<SqlGeometry>();
            for (int er = 1; er <= ring.STNumPoints(); er++)
            {
                SqlGeometry point = ring.STPointN(er);
                double stx = double.Parse(point.STX.ToString());
                double xp = .01098632 / Math.Pow(2, level - 7);
                double x = Convert.ToInt32(Math.Abs(stx) / xp) * xp;
                double sty = double.Parse(point.STY.ToString());
                double yp = .009698274 / Math.Pow(2, level - 7);
                double y = Convert.ToInt32(Math.Abs(sty) / yp) * yp;
                SqlChars ppWkt = new SqlChars("POINT(-" + x + " " + y + ")");
                SqlGeometry processedPoint = SqlGeometry.STPointFromText(ppWkt, 4326);
                //SqlGeometry processedPoint = SqlGeometry.STPointFromText(ppWkt, 4326);
                //processedPoints[er - 1] = processedPoint;
                processedPoints.Add(processedPoint);
            }
            return processedPoints;
        }

        public static List<interiorRing> GetInteriorRings(SqlGeometry individualGeom, int level)
        {
            SqlInt32 numOfInteriorRings = individualGeom.STNumInteriorRing();
            List<interiorRing> irList = new List<interiorRing>();
            if (numOfInteriorRings >= 1)//has interiors
            {
                for (int ir = 1; ir <= numOfInteriorRings; ir++)//looping through each interior ring if there are any
                {
                    interiorRing irNew = new interiorRing();
                    irNew.irPoints = GetPolyPoints(individualGeom.STInteriorRingN(ir), level, false);
                    irList.Add(irNew);
                }
            }
            return irList;
        }


        public class DistinctPointComparer : IEqualityComparer<SqlGeometry>
        {

            public bool Equals(SqlGeometry firstPt, SqlGeometry secondPt)
            {
                return firstPt.STX.ToString() == secondPt.STX.ToString() &&
                    firstPt.STY.ToString() == secondPt.STY.ToString();
            }

            public int GetHashCode(SqlGeometry obj)
            {
                return obj.STX.ToString().GetHashCode() ^
                    obj.STY.ToString().GetHashCode();
            }
        }


    }
}
